package mk.finki.chemistry.dice;

import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Vector;

import org.jgap.BaseChromosome;
import org.jgap.Configuration;
import org.jgap.FitnessEvaluator;
import org.jgap.FitnessFunction;
import org.jgap.Gene;
import org.jgap.Genotype;
import org.jgap.IChromosome;
import org.jgap.IChromosomePool;
import org.jgap.ICloneHandler;
import org.jgap.IGeneConstraintChecker;
import org.jgap.IJGAPFactory;
import org.jgap.InvalidConfigurationException;
import org.jgap.RandomGenerator;
import org.jgap.UnsupportedRepresentationException;

/**
 * Chromosomes represent potential solutions and consist of a fixed-length
 * collection of genes. Each gene represents a discrete part of the solution.
 * Each gene in the Chromosome may be backed by a different concrete
 * implementation of the Gene interface, but all genes in a respective position
 * (locus) must share the same concrete implementation across Chromosomes within
 * a single population (genotype). In other words, gene 1 in a chromosome must
 * share the same concrete implementation as gene 1 in all other chromosomes in
 * the population.
 * 
 * @author Neil Rotstan
 * @author Klaus Meffert
 * @since 1.0
 */
public class Chromosome extends BaseChromosome {
	/** String containing the CVS revision. Read out via reflection! */
	private final static String CVS_REVISION = "$Revision: 1.103 $";

	/**
	 * Application-specific data that is attached to this Chromosome. This data
	 * may assist the application in evaluating this Chromosome in the fitness
	 * function. JGAP does not operate on the data, aside from allowing it to be
	 * set and retrieved, and considering it with comparations (if user opted in
	 * to do so).
	 */
	private Object m_applicationData;

	/**
	 * Holds multiobjective values.
	 * 
	 * @since 2.6
	 * @todo move to new subclass of Chromosome (and introduce new interface
	 *       IMultiObjective with that)
	 */
	private List m_multiObjective;

	/**
	 * Keeps track of whether or not this Chromosome has been selected by the
	 * natural selector to move on to the next generation.
	 */
	private boolean m_isSelectedForNextGeneration;

	/**
	 * Stores the fitness value of this Chromosome as determined by the active
	 * fitness function. A value of -1 indicates that this field has not yet been
	 * set with this Chromosome's fitness values (valid fitness values are always
	 * positive).
	 * 
	 * @since 2.0 (until 1.1: type int)
	 */
	protected double m_fitnessValue = FitnessFunction.NO_FITNESS_VALUE;

	/**
	 * Method compareTo(): Should we also consider the application data when
	 * comparing? Default is "false", as "true" means a Chromosome's losing its
	 * identity when application data is set differently!
	 * 
	 * @since 2.2
	 */
	private boolean m_compareAppData;

	/**
	 * Optional helper class for checking if a given allele value to be set for a
	 * given gene is valid. If not, the allele value may not be set for the gene
	 * or the gene type (e.g. IntegerGene) is not allowed in general!
	 * 
	 * @since 2.5
	 */
	private IGeneConstraintChecker m_geneAlleleChecker;

	/**
	 * If set to true the method getFitnessValue() will always (re-)calculate the
	 * fitness value. This may be necessary in case of environments where the
	 * state changes without the chromosome to notice
	 * 
	 * @since 3.2.2
	 */
	private boolean m_alwaysCalculate;

	/**
	 * Default constructor, only provided for dynamic instantiation.
	 * <p>
	 * Attention: The configuration used is the one set with the static method
	 * Genotype.setConfiguration.
	 * 
	 * @throws InvalidConfigurationException
	 * 
	 * @author Klaus Meffert
	 * @since 2.4
	 */
	public Chromosome() throws InvalidConfigurationException {
		this(Genotype.getStaticConfiguration());
	}

	/**
	 * Constructor, provided for dynamic or minimal instantiation.
	 * 
	 * @param a_configuration
	 *          the configuration to use
	 * @throws InvalidConfigurationException
	 * 
	 * @author Klaus Meffert
	 * @since 3.0
	 */
	public Chromosome(final Configuration a_configuration) throws InvalidConfigurationException {
		super(a_configuration);
		m_alwaysCalculate = a_configuration.isAlwaysCalculateFitness();
	}

	/**
	 * Constructor, provided for instantiation via persistent representation.
	 * 
	 * @param a_configuration
	 *          the configuration to use
	 * @param a_persistentRepresentatuion
	 *          valid persistent representation that was most likely obtained via
	 *          getPersistentRepresentation()
	 * @throws InvalidConfigurationException
	 * @throws UnsupportedRepresentationException
	 * 
	 * @author Klaus Meffert
	 * @since 3.2
	 */
	public Chromosome(final Configuration a_configuration, String a_persistentRepresentatuion)
			throws InvalidConfigurationException, UnsupportedRepresentationException {
		this(a_configuration);
		setValueFromPersistentRepresentation(a_persistentRepresentatuion);
	}

	/**
	 * Constructor for specifying the number of genes.
	 * 
	 * @param a_configuration
	 *          the configuration to use
	 * @param a_desiredSize
	 *          number of genes the chromosome contains of
	 * @throws InvalidConfigurationException
	 * 
	 * @author Klaus Meffert
	 * @since 2.2
	 */
	public Chromosome(final Configuration a_configuration, final int a_desiredSize)
			throws InvalidConfigurationException {
		this(a_configuration);
		if (a_desiredSize <= 0) {
			throw new IllegalArgumentException("Chromosome size must be greater than zero");
		}
		setGenes(new Gene[a_desiredSize]);
	}

	/**
	 * Constructs a Chromosome of the given size separate from any specific
	 * Configuration. This constructor will use the given sample Gene to construct
	 * a new Chromosome instance containing genes all of the same type as the
	 * sample Gene. This can be useful for constructing sample chromosomes that
	 * use the same Gene type for all of their genes and that are to be used to
	 * setup a Configuration object.
	 * 
	 * @param a_configuration
	 *          the configuration to use
	 * @param a_sampleGene
	 *          a concrete sampleGene instance that will be used as a template for
	 *          all of the genes in this Chromosome
	 * @param a_desiredSize
	 *          the desired size (number of genes) of this Chromosome
	 * @throws InvalidConfigurationException
	 * 
	 * @author Neil Rotstan
	 * @author Klaus Meffert
	 * @since 1.0
	 */
	public Chromosome(final Configuration a_configuration, final Gene a_sampleGene,
			final int a_desiredSize) throws InvalidConfigurationException {
		this(a_configuration, a_desiredSize);
		initFromGene(a_sampleGene);
	}

	public Chromosome(final Configuration a_configuration, Gene a_sampleGene, int a_desiredSize,
			IGeneConstraintChecker a_constraintChecker) throws InvalidConfigurationException {
		this(a_configuration, a_desiredSize);
		initFromGene(a_sampleGene);
		setConstraintChecker(a_constraintChecker);
	}

	protected void initFromGene(Gene a_sampleGene) {
		// Do sanity checking to make sure the parameters we were
		// given are valid.
		// ------------------------------------------------------
		if (a_sampleGene == null) {
			throw new IllegalArgumentException("Sample Gene cannot be null.");
		}
		// Populate the array of genes it with new Gene instances
		// created from the sample gene.
		// ------------------------------------------------------
		int size = size();
		for (int i = 0; i < size; i++) {
			setGene(i, a_sampleGene.newGene());
		}
	}

	/**
	 * Constructs a Chromosome separate from any specific Configuration. This can
	 * be useful for constructing sample chromosomes that are to be used to setup
	 * a Configuration object.
	 * 
	 * @param a_configuration
	 *          the configuration to use
	 * @param a_initialGenes
	 *          the initial genes of this Chromosome
	 * @throws InvalidConfigurationException
	 * 
	 * @author Neil Rotstan
	 * @since 1.0
	 */
	public Chromosome(final Configuration a_configuration, Gene[] a_initialGenes)
			throws InvalidConfigurationException {
		this(a_configuration, a_initialGenes == null ? 0 : a_initialGenes.length);
		checkGenes(a_initialGenes);
		setGenes(a_initialGenes);
	}

	/**
	 * Constructs a Chromosome separate from any specific Configuration. This can
	 * be useful for constructing sample chromosomes that are to be used to setup
	 * a Configuration object. Additionally, a constraint checker can be
	 * specified. It is used right here to verify the validity of the gene types
	 * supplied.
	 * 
	 * @param a_configuration
	 *          the configuration to use
	 * @param a_initialGenes
	 *          the initial genes of this Chromosome
	 * @param a_constraintChecker
	 *          constraint checker to use
	 * @throws InvalidConfigurationException
	 *           in case the constraint checker reports a configuration error
	 * 
	 * @author Klaus Meffert
	 * @since 2.5
	 */
	public Chromosome(final Configuration a_configuration, Gene[] a_initialGenes,
			IGeneConstraintChecker a_constraintChecker) throws InvalidConfigurationException {
		this(a_configuration, a_initialGenes.length);
		checkGenes(a_initialGenes);
		setGenes(a_initialGenes);
		setConstraintChecker(a_constraintChecker);
	}

	/**
	 * Helper: called by constructors only to verify the initial genes.
	 * 
	 * @param a_initialGenes
	 *          the initial genes of this Chromosome to verify
	 * 
	 * @author Klaus Meffert
	 * @since 2.5
	 */
	protected void checkGenes(Gene[] a_initialGenes) {
		// Sanity checks: make sure the genes array isn't null and
		// that none of the genes contained within it are null.
		// Check against null already done in constructors!
		// -------------------------------------------------------
		for (int i = 0; i < a_initialGenes.length; i++) {
			if (a_initialGenes[i] == null) {
				throw new IllegalArgumentException("The gene at index " + i + " in the given array of "
						+ "genes was found to be null. No gene in the array " + "may be null.");
			}
		}
	}

	/**
	 * Returns a copy of this Chromosome. The returned instance can evolve
	 * independently of this instance. Note that, if possible, this method will
	 * first attempt to acquire a Chromosome instance from the active
	 * ChromosomePool (if any) and set its value appropriately before returning
	 * it. If that is not possible, then a new Chromosome instance will be
	 * constructed and its value set appropriately before returning.
	 * 
	 * @return copy of this Chromosome
	 * @throws IllegalStateException
	 *           instead of CloneNotSupportedException
	 * 
	 * @author Neil Rotstan
	 * @author Klaus Meffert
	 * @since 1.0
	 */
	public synchronized Object clone() {
		// Before doing anything, make sure that a Configuration object
		// has been set on this Chromosome. If not, then throw an
		// IllegalStateException.
		// ------------------------------------------------------------
		if (getConfiguration() == null) {
			throw new IllegalStateException("The active Configuration object must be set on this "
					+ "Chromosome prior to invocation of the clone() method.");
		}
		IChromosome copy = null;
		// Now, first see if we can pull a Chromosome from the pool and just
		// set its gene values (alleles) appropriately.
		// ------------------------------------------------------------
		IChromosomePool pool = getConfiguration().getChromosomePool();
		if (pool != null) {
			copy = pool.acquireChromosome();
			if (copy != null) {
				Gene[] genes = copy.getGenes();
				for (int i = 0; i < size(); i++) {
					genes[i].setAllele(getGene(i).getAllele());
				}
			}
		}
		try {
			if (copy == null) {
				// We couldn't fetch a Chromosome from the pool, so we need to create
				// a new one. First we make a copy of each of the Genes. We explicity
				// use the Gene at each respective gene location (locus) to create the
				// new Gene that is to occupy that same locus in the new Chromosome.
				// -------------------------------------------------------------------
				int size = size();
				if (size > 0) {
					Gene[] copyOfGenes = new Gene[size];
					for (int i = 0; i < size; i++) {
						copyOfGenes[i] = getGene(i).newGene();
						Object allele = getGene(i).getAllele();
						if (allele != null) {
							IJGAPFactory factory = getConfiguration().getJGAPFactory();
							if (factory != null) {
								ICloneHandler cloner = factory.getCloneHandlerFor(allele, allele.getClass());
								if (cloner != null) {
									try {
										allele = cloner.perform(allele, null, this);
									} catch (Exception ex) {
										throw new RuntimeException(ex);
									}
								}
							}
						}
						copyOfGenes[i].setAllele(allele);
					}
					// Now construct a new Chromosome with the copies of the genes and
					// return it. Also clone the IApplicationData object later on.
					// ---------------------------------------------------------------
					if (getClass() == Chromosome.class) {
						copy = new Chromosome(getConfiguration(), copyOfGenes);
					} else {
						try {
							// Try dynamic call of constructor. Attention: This may not
							// work for inner classes!
							// --------------------------------------------------------
							Constructor[] constr = getClass().getDeclaredConstructors();
							if (constr != null) {
								for (int i = 0; i < constr.length; i++) {
									Class[] params = constr[i].getParameterTypes();
									if (params != null && params.length == 1) {
										if (params[0] == Configuration.class) {
											copy = (IChromosome) constr[i]
													.newInstance(new Object[] { getConfiguration() });
											copy.setGenes(copyOfGenes);
										}
									}
								}
							}
							if (copy == null) {
								// Enforce alternative cloning to get at least something.
								// ------------------------------------------------------
								throw new Exception("No appropriate constructor for cloning found.");
							}
						} catch (Exception ex) {
							ex.printStackTrace();
							// Do it the old way with the danger of getting a stack overflow.
							// --------------------------------------------------------------
							copy = (IChromosome) getConfiguration().getSampleChromosome().clone();
							copy.setGenes(copyOfGenes);
						}
					}
				} else {
					if (getClass() == Chromosome.class) {
						copy = new Chromosome(getConfiguration());
					} else {
						copy = (IChromosome) getConfiguration().getSampleChromosome().clone();
					}
				}
			}
			copy.setFitnessValue(m_fitnessValue);
			// Clone constraint checker.
			// -------------------------
			copy.setConstraintChecker(getConstraintChecker());
		} catch (InvalidConfigurationException iex) {
			throw new IllegalStateException(iex.getMessage());
		}
		// Also clone the IApplicationData object.
		// ---------------------------------------
		try {
			copy.setApplicationData(cloneObject(getApplicationData()));
		} catch (Exception ex) {
			throw new IllegalStateException(ex.getMessage());
		}
		// Clone multi-objective object if necessary and possible.
		// -------------------------------------------------------
		if (m_multiObjective != null) {
			if (getClass() == Chromosome.class) {
				try {
					((Chromosome) copy).setMultiObjectives((List) cloneObject(m_multiObjective));
				} catch (Exception ex) {
					throw new IllegalStateException(ex.getMessage());
				}
			}
		}
		return copy;
	}

	/**
	 * Clones an object by using clone handlers. If no deep cloning possible, then
	 * return the reference.
	 * 
	 * @param a_object
	 *          the object to clone
	 * @return the cloned object, or the object itself if no coning supported
	 * @throws Exception
	 * 
	 * @author Klaus Meffert
	 * @since 2.6
	 */
	protected Object cloneObject(Object a_object) throws Exception {
		return cloneObject(getConfiguration(), a_object, this);
	}

	/**
	 * Static convenience method. Clones an object by using clone handlers. If no
	 * deep cloning possible, then return the reference.
	 * 
	 * @param a_config
	 *          a valid configuration to obtain the JGAPFactory from
	 * @param a_object
	 *          the object to clone
	 * @param a_master
	 *          the super object of a_object, e.g. a chromosome in case of
	 *          application to be cloned
	 * @return the cloned object, or the object itself if no coning supported
	 * @throws Exception
	 * 
	 * @author Klaus Meffert
	 * @since 3.5
	 */
	public static Object cloneObject(Configuration a_config, Object a_object, Object a_master)
			throws Exception {
		if (a_object == null) {
			return null;
		}
		// Try to clone via a registered clone handler.
		// --------------------------------------------
		ICloneHandler cloner = a_config.getJGAPFactory().getCloneHandlerFor(a_object,
				a_object.getClass());
		if (cloner != null) {
			return cloner.perform(a_object, null, a_master);
		} else {
			// No cloning supported, so just return the reference.
			// ---------------------------------------------------
			return a_object;
		}
	}

	/**
	 * Retrieves the fitness value of this Chromosome, as determined by the active
	 * fitness function. If a bulk fitness function is in use and has not yet
	 * assigned a fitness value to this Chromosome, then -1 is returned.
	 * <p>
	 * Attention: should not be called from toString() as the fitness value would
	 * be computed if it was initial!
	 * 
	 * @return a positive double value representing the fitness of this
	 *         Chromosome, or -1 if a bulk fitness function is in use and has not
	 *         yet assigned a fitness value to this Chromosome
	 * 
	 * @author Neil Rotstan
	 * @author Klaus Meffert
	 * @since 2.0
	 */
	public double getFitnessValue() {
		if (m_fitnessValue >= 0.000d && !m_alwaysCalculate) {
			return m_fitnessValue;
		} else {
			return calcFitnessValue();
		}
	}

	/**
	 * @return the lastly computed fitness value, or
	 *         FitnessFunction.NO_FITNESS_VALUE in case no value has been computed
	 *         yet.
	 * 
	 * @author Klaus Meffert
	 */
	public double getFitnessValueDirectly() {
		return m_fitnessValue;
	}

	/**
	 * @return fitness value of this chromosome determined via the registered
	 *         fitness function
	 * 
	 * @author Klaus Meffert
	 * @since 2.4
	 */
	protected double calcFitnessValue() {
		if (getConfiguration() != null) {
			FitnessFunction normalFitnessFunction = getConfiguration().getFitnessFunction();
			if (normalFitnessFunction != null) {
				// Grab the "normal" fitness function and ask it to calculate our
				// fitness value.
				// --------------------------------------------------------------
				m_fitnessValue = normalFitnessFunction.getFitnessValue(this);
			}
		}
		return m_fitnessValue;
	}

	/**
	 * Sets the fitness value of this Chromosome. This method is for use by bulk
	 * fitness functions and should not be invokved from anything else (except
	 * test cases).
	 * 
	 * @param a_newFitnessValue
	 *          a positive integer representing the fitness of this Chromosome
	 * 
	 * @author Neil Rotstan
	 * @since 1.0
	 */
	public void setFitnessValue(double a_newFitnessValue) {
		if (a_newFitnessValue >= 0 && Math.abs(m_fitnessValue - a_newFitnessValue) > 0.0000001) {
			m_fitnessValue = a_newFitnessValue;
		}
	}

	/**
	 * Sets the fitness value of this Chromosome directly without any constraint
	 * checks, conversions or checks. Only use if you know what you do.
	 * 
	 * @param a_newFitnessValue
	 *          a positive integer representing the fitness of this Chromosome
	 * 
	 * @author Klaus Meffert
	 */
	public void setFitnessValueDirectly(double a_newFitnessValue) {
		m_fitnessValue = a_newFitnessValue;
	}

	/**
	 * @return a string representation of this Chromosome, useful for display
	 *         purposes
	 * 
	 * @author Neil Rotstan
	 * @author Klaus Meffert
	 * @since 1.0
	 */
	public String toString() {
		StringBuffer representation = new StringBuffer();
		representation.append(S_SIZE + ":" + size());
		// Don't use getFitnessValue() here as it would then be initialized if
		// it was not. We want to capture the current state here!
		// -------------------------------------------------------------------
		representation.append(", " + S_FITNESS_VALUE + ":" + m_fitnessValue);
		representation.append(", " + S_ALLELES + ":");
		representation.append("[");
		// Append the representations of each of the genes' alleles.
		// ---------------------------------------------------------
		int size = size();
		for (int i = 0; i < size; i++) {
			if (i > 0) {
				representation.append(", ");
			}
			if (getGene(i) == null) {
				representation.append("null");
			} else {
				representation.append(getGene(i).toString());
			}
		}
		representation.append("]");
		String appData;
		if (getApplicationData() != null) {
			appData = getApplicationData().toString();
		} else {
			appData = "null";
		}
		representation.append(", " + S_APPLICATION_DATA + ":" + appData);
		return representation.toString();
	}

	/**
	 * Convenience method that returns a new Chromosome instance with its genes
	 * values (alleles) randomized. Note that, if possible, this method will
	 * acquire a Chromosome instance from the active ChromosomePool (if any) and
	 * then randomize its gene values before returning it. If a Chromosome cannot
	 * be acquired from the pool, then a new instance will be constructed and its
	 * gene values randomized before returning it.
	 * 
	 * @param a_configuration
	 *          the configuration to use
	 * @return randomly initialized Chromosome
	 * @throws InvalidConfigurationException
	 *           if the given Configuration instance is invalid
	 * @throws IllegalArgumentException
	 *           if the given Configuration instance is null
	 * 
	 * @author Neil Rotstan
	 * @author Klaus Meffert
	 * @since 1.0
	 */
	public static IChromosome randomInitialChromosome(Configuration a_configuration)
			throws InvalidConfigurationException {
		// Sanity check: make sure the given configuration isn't null.
		// -----------------------------------------------------------
		if (a_configuration == null) {
			throw new IllegalArgumentException("Configuration instance must not be null");
		}
		// Lock the configuration settings so that they can't be changed
		// from now on.
		// -------------------------------------------------------------
		a_configuration.lockSettings();
		// First see if we can get a Chromosome instance from the pool.
		// If we can, we'll randomize its gene values (alleles) and then
		// return it.
		// -------------------------------------------------------------
		IChromosomePool pool = a_configuration.getChromosomePool();
		if (pool != null) {
			IChromosome randomChromosome = pool.acquireChromosome();
			if (randomChromosome != null) {
				Gene[] genes = randomChromosome.getGenes();
				RandomGenerator generator = a_configuration.getRandomGenerator();
				for (int i = 0; i < genes.length; i++) {
					genes[i].setToRandomValue(generator);
					/** @todo what about Gene's energy? */
				}
				randomChromosome.setFitnessValueDirectly(FitnessFunction.NO_FITNESS_VALUE);
				return randomChromosome;
			}
		}
		// We weren't able to get a Chromosome from the pool, so we have to
		// construct a new instance and build it from scratch.
		// ------------------------------------------------------------------
		IChromosome sampleChromosome = a_configuration.getSampleChromosome();
		sampleChromosome.setFitnessValue(FitnessFunction.NO_FITNESS_VALUE);
		Gene[] sampleGenes = sampleChromosome.getGenes();
		Gene[] newGenes = new Gene[sampleGenes.length];
		RandomGenerator generator = a_configuration.getRandomGenerator();
		for (int i = 0; i < newGenes.length; i++) {
			// We use the newGene() method on each of the genes in the
			// sample Chromosome to generate our new Gene instances for
			// the Chromosome we're returning. This guarantees that the
			// new Genes are setup with all of the correct internal state
			// for the respective gene position they're going to inhabit.
			// -----------------------------------------------------------
			newGenes[i] = sampleGenes[i].newGene();
			// If application data is set, try to clone it as well.
			// ----------------------------------------------------
			Object appData = sampleGenes[i].getApplicationData();
			if (appData != null) {
				try {
					cloneObject(a_configuration, appData, sampleChromosome);
				} catch (Exception ex) {
					throw new InvalidConfigurationException("Application data of "
							+ "sample chromsome is not cloneable", ex);
				}
			}
			// Set the gene's value (allele) to a random value.
			// ------------------------------------------------
			newGenes[i].setToRandomValue(generator);
			/** @todo what about Gene's energy? */
		}
		// Finally, construct the new chromosome with the new random
		// genes values and return it.
		// ---------------------------------------------------------
		return new Chromosome(a_configuration, newGenes);
	}

	/**
	 * Compares this Chromosome against the specified object. The result is true
	 * if and the argument is an instance of the Chromosome class and has a set of
	 * genes equal to this one.
	 * 
	 * @param other
	 *          the object to compare against
	 * @return true: if the objects are the same, false otherwise
	 * 
	 * @author Neil Rotstan
	 * @author Klaus Meffert
	 * @since 1.0
	 */
	public boolean equals(Object other) {
		// If class is not equal, return false. Therefor catch
		// ClasscastException's. The cleaner way (commented out below) would
		// be too slow, indeed.
		// -----------------------------------------------------------------
		/*
		 * if (other != null && !this.getClass ().getName ().equals (other.getClass
		 * ().getName ())) { return false; }
		 */
		try {
			return compareTo(other) == 0;
		} catch (ClassCastException cex) {
			return false;
		}
	}

	/**
	 * Retrieve a hash code for this Chromosome. Does not considers the order of
	 * the Genes for all cases (especially when gene is empty).
	 * 
	 * @return the hash code of this Chromosome
	 * 
	 * @author Neil Rotstan
	 * @author Klaus Meffert
	 * @since 1.0
	 */
	public int hashCode() {
		// Do what java.util.AbstractList does.
		// ------------------------------------
		int geneHashcode;
		int hashCode = 1;
		if (getGenes() != null) {
			int size = size();
			for (int i = 0; i < size; i++) {
				Gene gene = getGene(i);
				if (gene == null) {
					geneHashcode = -55;
				} else {
					geneHashcode = gene.hashCode();
				}
				hashCode = 31 * hashCode + geneHashcode;
			}
		}
		return hashCode;
	}

	/**
	 * Compares the given Chromosome to this Chromosome. This chromosome is
	 * considered to be "less than" the given chromosome if it has a fewer number
	 * of genes or if any of its gene values (alleles) are less than their
	 * corresponding gene values in the other chromosome.
	 * 
	 * @param other
	 *          the Chromosome against which to compare this chromosome
	 * @return a negative number if this chromosome is "less than" the given
	 *         chromosome, zero if they are equal to each other, and a positive
	 *         number if this chromosome is "greater than" the given chromosome
	 * 
	 * @author Neil Rotstan
	 * @author Klaus Meffert
	 * @since 1.0
	 */
	public int compareTo(Object other) {
		// First, if the other Chromosome is null, then this chromosome is
		// automatically the "greater" Chromosome.
		// ---------------------------------------------------------------
		if (other == null) {
			return 1;
		}
		int size = size();
		IChromosome otherChromosome = (IChromosome) other;
		Gene[] otherGenes = otherChromosome.getGenes();
		// If the other Chromosome doesn't have the same number of genes,
		// then whichever has more is the "greater" Chromosome.
		// --------------------------------------------------------------
		if (otherChromosome.size() != size) {
			return size() - otherChromosome.size();
		}
		// Next, compare the gene values (alleles) for differences. If
		// one of the genes is not equal, then we return the result of its
		// comparison.
		// ---------------------------------------------------------------
		for (int i = 0; i < size; i++) {
			int comparison = getGene(i).compareTo(otherGenes[i]);
			if (comparison != 0) {
				return comparison;
			}
		}
		// Compare current fitness value.
		// ------------------------------
		if (m_fitnessValue != otherChromosome.getFitnessValueDirectly()) {
			FitnessEvaluator eval = getConfiguration().getFitnessEvaluator();
			if (eval != null) {
				if (eval.isFitter(m_fitnessValue, otherChromosome.getFitnessValueDirectly())) {
					return 1;
				} else {
					return -1;
				}
			} else {
				// undetermined order, but unequal!
				// --------------------------------
				return -1;
			}
		}
		if (m_compareAppData) {
			// Compare application data.
			// -------------------------
			if (getApplicationData() == null) {
				if (otherChromosome.getApplicationData() != null) {
					return -1;
				}
			} else if (otherChromosome.getApplicationData() == null) {
				return 1;
			} else {
				if (getApplicationData() instanceof Comparable) {
					try {
						return ((Comparable) getApplicationData()).compareTo(otherChromosome
								.getApplicationData());
					} catch (ClassCastException cex) {
						/** @todo improve */
						return -1;
					}
				} else {
					return getApplicationData().getClass().getName()
							.compareTo(otherChromosome.getApplicationData().getClass().getName());
				}
			}
		}
		// Everything is equal. Return zero.
		// ---------------------------------
		return 0;
	}

	/**
	 * Sets whether this Chromosome has been selected by the natural selector to
	 * continue to the next generation or manually (e.g. via an add-method).
	 * 
	 * @param a_isSelected
	 *          true if this Chromosome has been selected, false otherwise
	 * 
	 * @author Neil Rotstan
	 * @since 1.0
	 */
	public void setIsSelectedForNextGeneration(boolean a_isSelected) {
		m_isSelectedForNextGeneration = a_isSelected;
	}

	/**
	 * Retrieves whether this Chromosome has been selected by the natural selector
	 * to continue to the next generation.
	 * 
	 * @return true if this Chromosome has been selected, false otherwise
	 * 
	 * @author Neil Rotstan
	 * @since 1.0
	 */
	public boolean isSelectedForNextGeneration() {
		return m_isSelectedForNextGeneration;
	}

	/**
	 * Invoked when this Chromosome is no longer needed and should perform any
	 * necessary cleanup. Note that this method will attempt to release this
	 * Chromosome instance to the active ChromosomePool, if any.
	 * 
	 * @author Neil Rotstan
	 * @since 1.0
	 */
	public void cleanup() {
		if (getConfiguration() == null) {
			throw new IllegalStateException("The active Configuration object must be set on this "
					+ "Chromosome prior to invocation of the cleanup() method.");
		}
		// First, reset our internal state.
		// --------------------------------
		m_fitnessValue = getConfiguration().getFitnessFunction().NO_FITNESS_VALUE;
		m_isSelectedForNextGeneration = false;
		// Next we want to try to release this Chromosome to a ChromosomePool
		// if one has been setup so that we can save a little time and memory
		// next time a Chromosome is needed.
		// ------------------------------------------------------------------
		// Now fetch the active ChromosomePool from the Configuration object
		// and, if the pool exists, release this Chromosome to it.
		// -----------------------------------------------------------------
		IChromosomePool pool = getConfiguration().getChromosomePool();
		if (pool != null) {
			// Note that the pool will take care of any gene cleanup for us,
			// so we don't need to worry about it here.
			// -------------------------------------------------------------
			pool.releaseChromosome(this);
		} else {
			// No pool is available, so we need to finish cleaning up, which
			// basically entails requesting each of our genes to clean
			// themselves up as well.
			// -------------------------------------------------------------
			for (int i = 0; i < size(); i++) {
				getGene(i).cleanup();
			}
		}
	}

	/**
	 * This sets the application-specific data that is attached to this
	 * Chromosome. Attaching application-specific data may be useful for some
	 * applications when it comes time to evaluate this Chromosome in the fitness
	 * function. JGAP ignores this data, except for cloning and comparison (latter
	 * only if opted in via setCompareApplicationData(..))
	 * 
	 * @param a_newData
	 *          the new application-specific data to attach to this Chromosome.
	 *          Should be an instance of IApplicationData
	 * 
	 * @author Neil Rotstan
	 * @since 1.1
	 */
	public void setApplicationData(Object a_newData) {
		m_applicationData = a_newData;
	}

	/**
	 * Retrieves the application-specific data that is attached to this
	 * Chromosome. Attaching application-specific data may be useful for some
	 * applications when it comes time to evaluate this Chromosome in the fitness
	 * function. JGAP ignores this data functionally.
	 * 
	 * @return the application-specific data previously attached to this
	 *         Chromosome, or null if there is no data attached
	 * 
	 * @author Neil Rotstan
	 * @since 1.1
	 */
	public Object getApplicationData() {
		return m_applicationData;
	}

	/**
	 * Sets the genes for the chromosome.
	 * 
	 * @param a_genes
	 *          the genes to set for the chromosome
	 * 
	 * @throws InvalidConfigurationException
	 *           in case constraint checker is provided
	 * 
	 * @author Klaus Meffert
	 */
	public void setGenes(Gene[] a_genes) throws InvalidConfigurationException {
		super.setGenes(a_genes);
		verify(getConstraintChecker());
	}

	/**
	 * Should we also consider the application data when comparing? Default is
	 * "false" as "true" means a Chromosome is losing its identity when
	 * application data is set differently!
	 * 
	 * @param a_doCompare
	 *          true: consider application data in method compareTo
	 * 
	 * @author Klaus Meffert
	 * @since 2.2
	 */
	public void setCompareApplicationData(boolean a_doCompare) {
		m_compareAppData = a_doCompare;
	}

	/*
	 * @return should we also consider the application data when comparing?
	 * 
	 * @author Klaus Meffert
	 * 
	 * @since 2.2
	 */
	public boolean isCompareApplicationData() {
		return m_compareAppData;
	}

	/**
	 * Sets the constraint checker to be used for this gene whenever method
	 * setAllele(Object) is called.
	 * 
	 * @param a_constraintChecker
	 *          the constraint checker to be set
	 * @throws InvalidConfigurationException
	 * 
	 * @author Klaus Meffert
	 * @since 2.5
	 */
	public void setConstraintChecker(IGeneConstraintChecker a_constraintChecker)
			throws InvalidConfigurationException {
		verify(a_constraintChecker);
		m_geneAlleleChecker = a_constraintChecker;
	}

	/**
	 * @return IGeneConstraintChecker the constraint checker to be used whenever
	 *         method setGenes(Gene[]) is called.
	 * 
	 * @author Klaus Meffert
	 * @since 2.5
	 */
	public IGeneConstraintChecker getConstraintChecker() {
		return m_geneAlleleChecker;
	}

	/**
	 * Verifies the state of the chromosome. Especially takes care of the given
	 * constraint checker.
	 * 
	 * @param a_constraintChecker
	 *          the constraint checker to verify
	 * 
	 * @throws InvalidConfigurationException
	 * 
	 * @author Klaus Meffert
	 * @since 2.5
	 */
	protected void verify(IGeneConstraintChecker a_constraintChecker)
			throws InvalidConfigurationException {
		if (a_constraintChecker != null && getGenes() != null) {
			int len = getGenes().length;
			for (int i = 0; i < len; i++) {
				Gene gene = getGene(i);
				if (!a_constraintChecker.verify(gene, null, this, i)) {
					throw new InvalidConfigurationException("The gene type " + gene.getClass().getName()
							+ " is not allowed to be used in the chromosome due to the"
							+ " constraint checker used.");
				}
			}
		}
	}

	// ------------------------------------
	// Begin of IInitializer implementation
	// ------------------------------------

	/** {@inheritDoc} */
	public boolean isHandlerFor(Object a_obj, Class a_class) {
		if (a_class == Chromosome.class) {
			return true;
		} else {
			return false;
		}
	}

	/** {@inheritDoc} */
	public Object perform(Object a_obj, Class a_class, Object a_params) throws Exception {
		return randomInitialChromosome(getConfiguration());
	}

	// ----------------------------------
	// End of IInitializer implementation
	// ----------------------------------
	public void setMultiObjectives(List a_values) {
		if (m_multiObjective == null) {
			m_multiObjective = new Vector();
		}
		m_multiObjective.clear();
		m_multiObjective.addAll(a_values);
	}

	public List getMultiObjectives() {
		return m_multiObjective;
	}

	double density;
	double thermalExpansion;
	double isothermalCompressibility;
	double molarSpecificHeat;

	public double getDensity() {
		return density;
	}

	public void setDensity(double density) {
		this.density = density;
	}

	public double getThermalExpansion() {
		return thermalExpansion;
	}

	public void setThermalExpansion(double thermalExpansion) {
		this.thermalExpansion = thermalExpansion;
	}

	public double getIsothermalCompressibility() {
		return isothermalCompressibility;
	}

	public void setIsothermalCompressibility(double isothermalCompressibility) {
		this.isothermalCompressibility = isothermalCompressibility;
	}

	public double getMolarSpecificHeat() {
		return molarSpecificHeat;
	}

	public void setMolarSpecificHeat(double molarSpecificHeat) {
		this.molarSpecificHeat = molarSpecificHeat;
	}
}
